/**
 * 
 */
package com.monkeyboy.security.config;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * @Description
 *
 * @author Gavin<br>
 *         2019年8月29日
 */
@Configuration
//认证服务器，这个注解告诉 Spring 这个应用是 OAuth2 的授权服务器，认证服务器，provider
//note:资源服务器主要就是通过token来判断是否已经认证了，认证服务器和资源服务器之间的token是关联的，需要配置token的存储模式（内存，数据库等等）
@EnableAuthorizationServer
public class MyAppAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	@Autowired
	private AuthenticationManager authenticationManager;
	@Autowired
	private UserDetailsService userDetailsService;

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Autowired
	private TokenStore tokenStore;
	@Autowired(required = false)
	private JwtAccessTokenConverter jwtAccessTokenConverter;
	@Autowired(required = false)
	private TokenEnhancer jwtTokenEnhancer;// 增强器

	/**
	 * 由于继承了AuthorizationServerConfigurerAdapter进行自定义配置token,所以需要重新设置authenticationManager
	 * ，userDetailsService。不继承会默认设置这两个的
	 * 
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints.tokenStore(tokenStore)// 设置token的存储方式
				.authenticationManager(authenticationManager)//
				.userDetailsService(userDetailsService);
		if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {// 如果是jwt的存储方式就需要如下配置
			TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
			List<TokenEnhancer> enhancers = new ArrayList<>();
			enhancers.add(jwtTokenEnhancer);
			enhancers.add(jwtAccessTokenConverter);
			enhancerChain.setTokenEnhancers(enhancers);
			endpoints.tokenEnhancer(enhancerChain).accessTokenConverter(jwtAccessTokenConverter);
		}
	}

	/**
	 * 配置给哪些用户发送令牌等相关信息，一般配置在配置文件中
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		//由于该应用只是给我们自己的应用来使用，是不允许给第三方的用户颁发appid的，所以不用存储到数据库中，直接存储在内存中就行
		InMemoryClientDetailsServiceBuilder builder = clients.inMemory();// clients信息存储在内存里面
		// 这儿可以通过builder这种循环的方式配置多个clients信息，把clients设置在配置文件中，这里就只需要读取信息然后for循环
		builder.withClient("gavin").secret("gavin")// 配置客户端密码，实际使用是存到数据库中的，第三方应用需要申请账号密码
				.accessTokenValiditySeconds(7200)// token有效期
				.authorizedGrantTypes("refresh_token", "password")//表示gavin来请求的话只能是refresh_token刷新token，或者password这种授权
				.scopes("all", "read", "write")//配置权限
				.and().withClient("多个client配置可以");
	}

}
